using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;

namespace Microsoft.Coding4Fun.TweetCraft
{
    /// <summary>
    /// Contains extension methods for strings.
    /// </summary>
    public static class StringExtensions
    {
        /// <summary>
        /// Returns a <see cref="SecureString"/> containing the string.
        /// </summary>
        /// <param name="value">The string.</param>
        /// <returns>An instance of the <see cref="SecureString"/> class.</returns>
        public static SecureString ToSecureString(this string value)
        {
            if (value == null) 
                throw new ArgumentNullException("value");

            var secureString = new SecureString();

            foreach (char c in value)
                secureString.AppendChar(c);

            secureString.MakeReadOnly();
            return secureString;
        }

        /// <summary>
        /// Returns a <see cref="String"/> containing the string.
        /// </summary>
        /// <param name="value">The secure string.</param>
        /// <returns>An instance of the <see cref="String"/> class.</returns>
        public static string Unwrap(this SecureString value)
        {
            if (value == null) 
                throw new ArgumentNullException("value");

            IntPtr ptr = Marshal.SecureStringToCoTaskMemUnicode(value);
            try
            {
                return Marshal.PtrToStringUni(ptr);
            }
            finally
            {
                Marshal.ZeroFreeCoTaskMemUnicode(ptr);
            }            
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="SecureString"/> is null or empty.
        /// </summary>
        /// <param name="value">The secure string.</param>
        /// <returns>True, if the secure string is null or empty; false otherwise.</returns>
        public static bool IsNullOrEmpty(this SecureString value)
        {
            return value == null || value.Length == 0;
        }

        /// <summary>
        /// Combines to strings using <see cref="Path.Combine"/>.
        /// </summary>
        /// <param name="path1">The first path.</param>
        /// <param name="path2">The second path.</param>
        /// <returns>A <see cref="string"/> containing the combined path.</returns>
        public static string Combine(this string path1, string path2)
        {
            if (path1 == null)
                throw new ArgumentNullException("path1");
            if (path2 == null)
                throw new ArgumentNullException("path2");

            return Path.Combine(path1, path2);
        }
        
        /// <summary>
        /// Returns the relative path of the absolute path related to the root path.
        /// </summary>
        /// <param name="absolutePath">The absolute path.</param>
        /// <param name="rootPath">The root path.</param>
        /// <returns>A <see cref="string"/> containing the relative path.</returns>
        public static string RelativeTo(this string absolutePath, string rootPath)
        {
            if (absolutePath == null)
                throw new ArgumentNullException("absolutePath");
            if (rootPath == null)
                throw new ArgumentNullException("rootPath");
            if (!absolutePath.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase))
                throw new ArgumentException(Strings.PathNotRootedInRootPath, "absolutePath");

            string relativePath = absolutePath.Substring(rootPath.Length);
            return relativePath.Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
        }

        /// <summary>
        /// Converts a string to a <see cref="Uri"/>.
        /// </summary>
        /// <param name="value">The value of the string.</param>
        /// <returns>An instance of the <see cref="Uri"/> class if the string is a valid uri; null otherwise.</returns>
        public static Uri ToUri(this string value)
        {
            if (value == null)
                return null;
            if (!Uri.IsWellFormedUriString(value, UriKind.Absolute))
                return null;

            return new Uri(value);
        }
    }
}